package com.falcon.authority.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.falcon.authority.domain.po.*;
import com.falcon.authority.domain.req.RoleApiUpdateRequest;
import com.falcon.authority.domain.req.RoleMenuUpdateRequest;
import com.falcon.authority.mapper.*;
import com.falcon.authority.service.IRoleService;
import com.falcon.core.domain.BasePO;
import com.falcon.core.service.AbsBaseService;
import com.swan.core.utils.collection.ListUtil;
import com.swan.core.utils.core.AssertUtil;
import com.swan.mybatis.condition.Condition;
import com.swan.mybatis.condition.OpType;
import com.swan.mybatis.mapper.mapper.IBaseMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RoleService extends AbsBaseService<RolePO> implements IRoleService {

    @Autowired
    private IRoleMapper roleMapper;

    @Autowired
    private IUserRoleMapper userRoleMapper;

    @Autowired
    private IApiMapper apiMapper;

    @Autowired
    private IMenuMapper menuMapper;

    @Autowired
    private IRoleApiMapper roleApiMapper;

    @Autowired
    private IRoleMenuMapper roleMenuMapper;

    @Override
    public IBaseMapper<Long, RolePO> getMapper() {
        return this.roleMapper;
    }

    @Override
    public List<RolePO> queryRoleListByUserId(Long userId) {

        List<UserRolePO> userRoleList = this.userRoleMapper.selectList(Condition.newInstance().and(UserRolePO.Fields.userId, OpType.equals, userId));
        if (userRoleList.isEmpty()) {
            return Collections.emptyList();
        }

        List<Long> roleIds = userRoleList.stream().map(UserRolePO::getRoleId).collect(Collectors.toList());

        List<RolePO> rolePOS = this.roleMapper.selectList(Condition.newInstance().and(BasePO.Fields.id, OpType.in, roleIds));

        return rolePOS;
    }


    @Override
    public List<RoleApiPO> queryRoleApis(Long roleId) {
        return this.roleApiMapper.selectListByRoleId(roleId);
    }

    @Override
    public List<RoleMenuPO> queryRoleMenus(Long roleId) {
        return this.roleMenuMapper.selectListByRoleId(roleId);
    }

    @Transactional
    @Override
    public void updateRoleMenus(RoleMenuUpdateRequest request) {

        // 校验角色
        RolePO rolePO = this.roleMapper.selectById(request.getRoleId());
        AssertUtil.notNull(rolePO, "角色不存在");

        // 校验权限
        List<MenuPO> menuPOS = this.menuMapper.selectListByIds(request.getMenuIds());
        List<Long> existIds = ListUtil.toList(menuPOS, MenuPO::getId);
        List<Long> notExistIds = ListUtil.subtract(request.getMenuIds(), existIds);
        AssertUtil.isEmpty(notExistIds, "Menu 不存在:"+notExistIds);

        // 查询当前角色
        List<RoleMenuPO> roleMenus = this.roleMenuMapper.selectListByRoleId(request.getRoleId());

        // 计算新增和删除的关系
        List<Long> originalIds = ListUtil.toList(roleMenus, RoleMenuPO::getMenuId);
        List<Long> newMenuIds = ListUtil.subtract(request.getMenuIds(), originalIds);
        List<Long> deleteMenuIds = ListUtil.subtract(originalIds, request.getMenuIds());

        // 新增
        if (CollUtil.isNotEmpty(newMenuIds)) {
            List<RoleMenuPO> newRoleMenus = newMenuIds.stream()
                    .map(menuId -> { RoleMenuPO roleApiPO = new RoleMenuPO();
                        roleApiPO.setRoleId(request.getRoleId());
                        roleApiPO.setMenuId(menuId);
                        roleApiPO.setCreator("TODO");
                        roleApiPO.setCreateTime(new Date());
                        return roleApiPO;}
                    )
                    .toList();
            this.roleMenuMapper.insertList(newRoleMenus);
        }

        // 删除
        if (CollUtil.isNotEmpty(deleteMenuIds)) {
            Condition deleteCondition = Condition.newInstance().ignoreEmptyString()
                    .and(RoleMenuPO.Fields.roleId, OpType.equals, request.getRoleId())
                    .and(RoleMenuPO.Fields.menuId, OpType.in, deleteMenuIds);
            this.roleMenuMapper.deleteList(deleteCondition);
        }
    }

    @Transactional
    @Override
    public void updateRoleApis(RoleApiUpdateRequest request) {

        // 校验角色
        RolePO rolePO = this.roleMapper.selectById(request.getRoleId());
        AssertUtil.notNull(rolePO, "角色不存在");

        // 校验权限
        List<ApiPO> apiPOS = this.apiMapper.selectListByIds(request.getApiIds());
        List<Long> existIds = ListUtil.toList(apiPOS, ApiPO::getId);
        Collection<Long> notExistIds = CollUtil.subtract(request.getApiIds(), existIds);
//        AssertUtil.isTrue(notExistIds.isEmpty(), "Api 不存在:"+notExistIds);

        // 查询当前角色
        List<RoleApiPO> roleApis = this.roleApiMapper.selectListByRoleId(request.getRoleId());

        // 计算新增和删除的关系
        List<Long> originalIds = ListUtil.toList(roleApis, RoleApiPO::getApiId);
        List<Long> newApiIds = ListUtil.subtract(request.getApiIds(), originalIds);
        List<Long> deleteApiIds = ListUtil.subtract(originalIds, request.getApiIds());

        // 新增
        if (CollUtil.isNotEmpty(newApiIds)) {
            List<RoleApiPO> newRoleApis = newApiIds.stream()
                    .map(apiId -> {
                                RoleApiPO roleApiPO = new RoleApiPO();
                                roleApiPO.setRoleId(request.getRoleId());
                                roleApiPO.setApiId(apiId);
                                roleApiPO.setCreator("TODO");
                                roleApiPO.setCreateTime(new Date());
                                return roleApiPO;
                            }
                    )
                    .toList();
            this.roleApiMapper.insertList(newRoleApis);
        }

        // 删除
        if (CollUtil.isNotEmpty(deleteApiIds)) {
            Condition deleteCondition = Condition.newInstance().ignoreEmptyString()
                    .and(RoleApiPO.Fields.roleId, OpType.equals, request.getRoleId())
                    .and(RoleApiPO.Fields.apiId, OpType.in, deleteApiIds);
            this.roleApiMapper.deleteList(deleteCondition);
        }

    }

}

